`include "../definitions.v"
module Memory(
	//input signal
	PCIn,
	ALUIn,
	WD,
	WE,
	//output signal
	Instr,
	RD,
	
	// Clock signal
	CLK
);
	input[31:0] PCIn,ALUIn,WD;
	input WE,CLK;
	output[31:0] Instr,RD;
	reg[31:0] Instr,RD;
	
	// Memory data
	reg[7:0] data[`MEMORY_SIZE:0];
	
	integer temp1,temp2;
	
	// Posedge for Read
	always @(posedge CLK) begin
		if(WE) begin
			temp1 = ALUIn;
			{data[temp1],data[temp1+1],data[temp1+2],data[temp1+3]} = WD;
		end
		//$display("time = %d,Clock = %b,WE = %b",$time,CLK,WE);
	end
	
	
	always @(negedge CLK) begin
		temp1 = PCIn;
		temp2 = ALUIn;
		Instr = {data[temp1],data[temp1+1],data[temp1+2],data[temp1+3]};
		RD 	  = {data[temp2],data[temp2+1],data[temp2+2],data[temp2+3]};
		//$display("time = %d,Clock = %b,Instr = %b",$time,CLK,Instr);
	end 

	// Initial All Memory Unit to Zero
	initial begin
		for(temp1 = 0 ; temp1 < `MEMORY_SIZE ; temp1 = temp1+1)
			data[temp1] = 0;
		RD = 0;
		Instr = 0;
	end

	parameter R0 = 5'd0;
	parameter R1 = 5'd1;
	parameter R2 = 5'd2;
	parameter R3 = 5'd3;
	parameter Z5 = 5'd0;
	parameter Z6 = 5'd0;
	
	parameter alu = 6'h0;
	parameter Addi = 6'h8;
	parameter Andi = 6'hc;
	parameter Lui  = 6'hf;
	parameter Ori  = 6'hd;
	parameter SW = 6'h2b;
	parameter LW = 6'h23;
	parameter Slti = 6'ha;
	parameter J    = 6'h2;

	parameter Add = 6'h20;
	parameter And = 6'h24; 
	parameter Or  = 6'h25;
	parameter Nor = 6'h27;
	parameter Sub = 6'h22;
	parameter Sll  = 6'h0;
	parameter Srl  = 6'h2;
	parameter Slt  = 6'h2a;
	parameter Beq  = 6'h4;
	parameter Bne  = 6'h5;
	parameter Jr   = 6'h8;
	parameter Jal  = 6'h3;

	parameter ResultAddr = 10000;
	
	//TestUnit
	initial begin
	
		/* XXXi test
		{data[0],data[1],data[2],data[3]} = { Ori, R0, R1, 16'd123 };
		{data[4],data[5],data[6],data[7]} = { SW, R0, R1, 16'd10000};
		{data[8],data[9],data[10],data[11]} = { J, 26'd2};
		/*/
		
		/* for lw test and forward and binary operator
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { LW, R0, R2, 16'd504 };
		{data[8],data[9],data[10],data[11]} = {alu,R1,R2,R3,Z5,Slt};
		{data[12],data[13],data[14],data[15]} = { SW, R0, R3, 16'd10000};
		{data[16],data[17],data[18],data[19]} = { J, 26'd4};
		{data[500],data[501],data[502],data[503]} = 32'd1;
		{data[504],data[505],data[506],data[507]} = 32'd3;
		/*/
		
		/* for Lui Test
		{data[0],data[1],data[2],data[3]} = {Lui,Z5,5'd1,16'd1};
		{data[4],data[5],data[6],data[7]} = {SW, R0, R1, 16'd10000};
		{data[8],data[9],data[10],data[11]} = { J, 26'd2};
		/*/
		
		/* for sll and srl test 
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { alu, R1, Z5, R2, 5'd2,Srl};
		{data[8],data[9],data[10],data[11]} = {SW, R0, R2, 16'd10000};
		{data[12],data[13],data[14],data[15]} = { J, 26'd3};
		{data[500],data[501],data[502],data[503]} = 32'd7;
		/*/
		
		/* for slti test
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { Slti, R1, R2, 16'd1};
		{data[8],data[9],data[10],data[11]} = {SW, R0, R2, 16'd10000};
		{data[12],data[13],data[14],data[15]} = { J, 26'd3};
		{data[500],data[501],data[502],data[503]} = 32'd7;
		/*/
		
		/* for beq test
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { LW, R0, R2, 16'd504 };
		{data[8],data[9],data[10],data[11]} = {Beq, R1, R2, 16'd247};//jump to 1000
		{data[12],data[13],data[14],data[15]} = { SW, R0, R0, 16'd10000};
		
		{data[500],data[501],data[502],data[503]} = 32'd3;
		{data[504],data[505],data[506],data[507]} = 32'd2;
		{data[1000],data[1001],data[1002],data[1003]} = {SW,R0,R1,16'd10000};
		/*/
		
		/* for j test
		{data[0],data[1],data[2],data[3]} = { J, 26'd125 };
		{data[4],data[5],data[6],data[7]} = { Addi, R0, R1, 16'd504 };
		{data[8],data[9],data[10],data[11]} = {SW, R0, R1, 16'd10000};
		
		{data[500],data[501],data[502],data[503]} = {Addi,R0,R1,16'd123};
		{data[504],data[505],data[506],data[507]} = {SW, R0, R1, 16'd10000};
		/*/
		
		/* for jr test
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { alu, R1, Z5, Z5, Z6, Jr};
		{data[8],data[9],data[10],data[11]} = {Addi, R0, R2, 16'd247};
		{data[12],data[13],data[14],data[15]} = { SW, R0, R2, 16'd10000};
		
		{data[500],data[501],data[502],data[503]} = 32'd504;
		{data[504],data[505],data[506],data[507]} = {SW, R0, R1, 16'd10000};
		/*/
		
		/* for jal test
		{data[0],data[1],data[2],data[3]} = { LW, R0, R1, 16'd500 };
		{data[4],data[5],data[6],data[7]} = { Jal, 26'd126 };
		{data[8],data[9],data[10],data[11]} = {Addi, R0, R2, 16'd247};
		{data[12],data[13],data[14],data[15]} = { SW, R0, R2, 16'd10000};
		
		{data[500],data[501],data[502],data[503]} = 32'd504;
		{data[504],data[505],data[506],data[507]} = {SW, R0, R1, 16'd10000};
		{data[508],data[509],data[510],data[511]} = {SW, R0, 5'd`RAREG,16'd10004};
		//*/
	
		$readmemh("mem.mif",data);
		#`HALT_TIME $display("ResultMemory");
		
		$display("result :%0d",{data[ResultAddr],data[ResultAddr+1],data[ResultAddr+2],data[ResultAddr+3]});
		// has value when jal
		$display("result :%0d",{data[ResultAddr+4],data[ResultAddr+5],data[ResultAddr+6],data[ResultAddr+7]});
		
		$finish;
	end

endmodule
